from hud import Hud
from menu_button import MenuButton
from panda3d.core import Vec2, Point3, Vec3, Vec4, BitMask32
import os
from direct.gui.DirectGui import *
from pandac.PandaModules import TextNode, WindowProperties, Filename
from map_selector import MapSelector
from object_const import ObjectConstants
from enemy_ai import EnemyAI
from game_stats import GameStats
from visual_object_3D import *
from picker import Picker
from dialog import Dialog
'''
	Keeps track of all the models and things for a singleplayer game
'''
class SinglePlayer:
	MAX_HEIGHT = 128
	MAX_WIDTH = 96
	BLOCK_SIZE = 5
	'''
		Creates the 3d portion of the single player mode
	'''
	def __init__(self, parent):
		self.parent = parent
		self.const = ObjectConstants()
		self.mapSelector = MapSelector(self.parent, self)
	
	def loadLevel(self):
		if (os.path.exists("maps/" + self.mapSelector.selected + ".lvl")):
		
			self.map = [[None for i in range(self.MAX_HEIGHT)] for j in range(self.MAX_WIDTH)]
			self.resources = dict()
			self.buildings = dict()
			self.units = dict()
			self.enemyUnits = dict()
			self.enemyBuildings = dict()
			
			self.gameStats = GameStats(self)
			
			self.picker = Picker(fromMask=BitMask32(0x01), collideWithGeom=True)
			self.selectorPicker = Picker(fromMask=BitMask32(0x02), collideWithGeom=True)
						
			self.count = 0
			self.movingUnit = False
			self.attacking = False
			self.placingBuilding = False
			
			self.mapDummy = self.parent.render.attachNewNode('map_dummy')
			self.terrainDummy = self.mapDummy.attachNewNode('terrain_dummy')
			self.startingCommandCenter = None
			self.objectModel = CommandCenter(self.parent, self.mapDummy, 'object_model')
			self.objectModel.hide()
			
			self.collisionTraverser = CollisionTraverser()
			self.parent.base.cTrav = self.collisionTraverser
			
			loadFile = open("maps/" + self.mapSelector.selected + ".lvl", 'r')
			
			mapFound = False
			modelsFound = False
			commandCenterFound = False
			i = 0
			for line in loadFile:
				if (line == "#command_center\n"):
					commandCenterFound = True
				elif (line == "#map\n"):
					mapFound = True
					i = -1
				elif (line == "#models\n"):
					modelsFound = True
				elif (commandCenterFound and not mapFound and not modelsFound):
					obj = line.split(" ")
					newModel = self.const.getObjectFromNo(self.const.COMMAND_CENTER, self.parent, self.mapDummy)
					newModel.setPos(Vec3(float(obj[0]), float(obj[1]), float(obj[2])))
					newModel.setGameStats(self)
					self.startingCommandCenter = newModel
					self.count += 1
					self.buildings['building_' + str(self.count)] = newModel
					newModel.setKey('building_' + str(self.count))
				elif (mapFound and commandCenterFound and not modelsFound):
					row = line.split(" ")
					for (j, cell) in enumerate(row):
						try:
							self.map[i][j] = self.loadModel(int(cell))
							self.map[i][j].setScale(self.BLOCK_SIZE)
							self.map[i][j].setPos((i - self.MAX_WIDTH/2.0)*self.BLOCK_SIZE, 0, (j - self.MAX_HEIGHT/2.0)*self.BLOCK_SIZE)
							self.map[i][j].setCollideMask(BitMask32(0x01))
						except ValueError:
							j -= 1
				elif(modelsFound):
					obj = line.split(" ")
					newModel = self.const.getObjectFromNo(int(obj[0]), self.parent, self.mapDummy)
					newModel.setPos(Vec3(float(obj[1]), float(obj[2]), float(obj[3])))
					if (self.const.isEnemy(newModel.type)):
						self.count += 1
						self.enemyUnits['enemyunit_' + str(self.count)] = newModel
						newModel.addCollisionDetection(self.collisionTraverser)
						newModel.setKey('enemyunit_' + str(self.count))
						newModel.setGameStats(self)
					elif (self.const.isEnemyBuilding(newModel.type)):
						self.count += 1
						self.enemyBuildings['enemybuilding_' + str(self.count)] = newModel
						newModel.setKey('enemybuilding_' + str(self.count))
						newModel.setGameStats(self)
					else:
						self.count += 1
						self.resources['resource_' + str(self.count)] = newModel
						newModel.setKey('resource_' + str(self.count))
						newModel.setGameStats(self)
				i += 1
				
			self.mapSelector.destroy()
			
			self.units['unit_' + str(self.count)] = self.const.getObjectFromNo(self.const.SCV, self.parent, self.mapDummy, 'unit_' + str(self.count))
			self.selection = self.units['unit_' + str(self.count)] 
			self.selection.setX(self.startingCommandCenter.getX() - 5)
			self.selection.setY(-self.selection.height/2)
			self.selection.setZ(self.startingCommandCenter.getZ() - 3)
			self.selection.setGameStats(self)
			self.selection.setCommandCenter(self.startingCommandCenter)
			self.selection.addCollisionDetection(self.collisionTraverser)
			self.hud = Hud(self.parent, self, self.units['unit_' + str(self.count)] )
			
			self.parent.cameraController.moveTo(self.startingCommandCenter.getPos())
			self.parent.cameraController.startMouseWatch()
			
			self.enemyAI = EnemyAI(self, self.enemyBuildings, self.enemyUnits)
			
			self.infoDialog = None
			self.infoDialog2 = None
			
			self.parent.accept("mouse1", self.makeNewSelection)
			self.parent.accept("h", self.showInformationDialog)
			
			self.count += 1
			self.units['unit_' + str(self.count)] = Marine(self.parent, self.mapDummy, 'unit_' + str(self.count))
			self.units['unit_' + str(self.count)].addCollisionDetection(self.collisionTraverser)
			self.units['unit_' + str(self.count)].setX(self.startingCommandCenter.getX() + 5)
			self.units['unit_' + str(self.count)].setY(-self.selection.height/2)
			self.units['unit_' + str(self.count)].setZ(self.startingCommandCenter.getZ() - 3)	
			self.units['unit_' + str(self.count)].setGameStats(self)
	'''
		Loads the correct item as a model and as a texture
	'''
	def loadModel(self, switch):
		texture = self.const.getTextureFromNo(switch, tuple=False)
		
		model = self.parent.loader.loadModelCopy("models/plane")
		texture = self.parent.loader.loadTexture(texture)
		model.setTexture(texture)
		model.setTransparency(1)
		model.reparentTo(self.terrainDummy)
		
		return model
	
	def toggleAttacking(self):
		self.attacking = not self.attacking
		self.movingUnit = False
		self.placingBuilding = False
		if (self.attacking):
			self.parent.accept("mouse1", self.selectionAttack)
			self.changeMouseCursor('attack')
		else:
			self.parent.accept("mouse1", self.makeNewSelection)
			self.changeMouseCursor('normal')
	
	def togglePositionSelection(self):
		self.movingUnit = not self.movingUnit
		self.attacking = False
		self.placingBuilding = False
		if (self.movingUnit):
			self.parent.accept("mouse1", self.selectionMoveTo)
			self.changeMouseCursor('move')
		else:
			self.parent.accept("mouse1", self.makeNewSelection)
			self.changeMouseCursor('normal')
	
	def selectionAttack(self):
		cEntry = self.selectorPicker.pick(self.mapDummy)
		if cEntry and self.selection:
			geomHit = cEntry.getIntoNodePath()
			key = geomHit.getParent().getTag('key')
			type = key.split('_')[0]
			if (type == 'enemyunit'):
				self.selection.attack(self.enemyUnits[key])
			elif (type == 'enemybuilding'):
				self.selection.attack(self.enemyBuildings[key])
			elif (type == 'resource' and self.selection.type == self.const.SCV):
				self.selection.attack(self.resources[key])
			self.toggleAttacking()
				
			
	
	def selectionMoveTo(self):
		cEntry = self.picker.pick(traverseRoot=self.terrainDummy)
		if cEntry and self.selection:
			pos = cEntry.getSurfacePoint(self.mapDummy) + Vec3(0,-self.selection.height/2, 0)
			self.selection.moveTo(pos)
			self.togglePositionSelection()
	
	def placeBuilding(self):
		if (self.placingBuilding):
			cEntry = self.picker.pick(traverseRoot=self.terrainDummy)
			if cEntry:
				newModel = self.const.getObjectFromTexture(str(self.objectModel.getTexture().getFilename()), self.parent, self.mapDummy)
				if (newModel):
					newModel.setPos(cEntry.getSurfacePoint(self.mapDummy)
						+ Vec3(0,-newModel.height/2, 0))
					
					price = newModel.getPrice()
					success = self.gameStats.purchase(price[0], price[1], 0)
					
					if (success):
						self.count += 1
						self.buildings['building_' + str(self.count)] = newModel
						newModel.setGameStats(self)
						newModel.setKey('building_' + str(self.count))
						self.selection.startBuilding(newModel)
					else:
						newModel.removeNode()
			
			self.endBuildingTask()
	
	def addBuildingTask(self, task):
		if (self.placingBuilding):
			if (self.const.isObject(task.building)):
				self.parent.accept("mouse1", self.placeBuilding)
				self.parent.accept("escape", self.endBuildingTask)
				self.objectModel.show()
				if (self.const.getTextureFromNo(task.building) is not self.objectModel.getTexture()):
					self.objectModel.removeNode()
					self.objectModel = self.const.getObjectFromNo(task.building, self.parent, self.mapDummy)
				cEntry = self.picker.pick(traverseRoot=self.terrainDummy)
				if cEntry:
					self.objectModel.setPos(cEntry.getSurfacePoint(self.mapDummy)
						+ Vec3(0,-self.objectModel.height/2, 0))
			return task.cont
		else:
			return task.done
	
	def endBuildingTask(self):
		self.placingBuilding = False
		self.objectModel.hide()
		self.objectModel.setY(200)
		self.parent.accept("mouse1", self.makeNewSelection)
		self.parent.ignore("escape")
	
	def makeNewSelection(self):
		cEntry = self.selectorPicker.pick(traverseRoot=self.mapDummy)
		if cEntry:
			geomHit = cEntry.getIntoNodePath()
			key = geomHit.getParent().getTag('key')
			type = key.split('_')[0]
			oldSelection = self.selection
			if (type == 'building'):
				self.selection = self.buildings[key]
				if (self.selection.clickable):
					buttons = self.selection.getButtons()
					self.hud.createMenu(buttons[0], buttons[1])
			elif (type == 'unit'):
				self.selection = self.units[key]
				buttons = self.selection.getButtons()
				buttons[0][0] = 'move'
				buttons[1][0] = (self.togglePositionSelection, [])
				
				buttons[0][2] = 'attack'
				buttons[1][2] = (self.toggleAttacking, [])
				
				if (self.selection.type == self.const.SCV):
					buttons[1][6] = (self.changeMenuToBuildings, [])
				
				self.hud.createMenu(buttons[0], buttons[1])
			elif (type == 'resource'):
				self.selection = self.resources[key]
				'''self.hud.createMenu(
					[
						'move', 
						'stop'
					], 
					[
						(self.togglePositionSelection, []),
						(self.selection.stop, [])
					]
				)'''
				
			
	'''
		Changes the mouse to a different cursor when 
		not over the hud 
	'''
	def changeMouseCursor(self, type):
		winprops = WindowProperties()
		if (type == 'attack' or type == 'move'):
			winprops.setCursorFilename(Filename.binaryFilename('models/menu/hud/move_cursor.ico'))
			self.parent.base.win.requestProperties(winprops)
		else:
			winprops.setCursorFilename(Filename.binaryFilename('models/menu/hud/cursor.cur'))
			self.parent.base.win.requestProperties(winprops)	
			
	'''
		Shows the buildings menu
	'''
	def changeMenuToBuildings(self):
		self.hud.createMenu(
			['command_center', 'barracks'],
			[
				(self.startBuildingTask, [self.const.COMMAND_CENTER]),
				(self.startBuildingTask, [self.const.BARRACKS]),	
			]
		)
		
	'''
		Starts the task for placing a building in the scene
	'''
	def startBuildingTask(self, building):
		self.placingBuilding = True
		task = self.parent.taskMgr.add(self.addBuildingTask, 'add_building_task')
		task.building = building
		
	def showInformationDialog(self):
		if (self.hud.dialog):
			self.hud.dialog.hide()
		
		if (self.infoDialog2):
			self.infoDialog2.hide()
	
		if (not self.infoDialog):
			self.infoDialog = Dialog(self.parent, 'information_dialog')
			self.infoDialog.addTextBox('In order to play: ', pos=Vec3(0,0, 0.30))
			self.infoDialog.addTextBox('Click a unit and then click the move command to make him move.', pos=Vec3(0, 0, 0.20))	
			self.infoDialog.addTextBox('Click a building and then click the unit command to create that new unit.', pos=Vec3(0, 0, 0.05))
			self.infoDialog.addOkCancel(self.infoDialog.hide, cancelCommand=self.nextInfoDialog, cancelText="Next Tip", cancelTextScale=Vec3(0.075,0.10,0.075))
		else:
			self.infoDialog.show()
			
	def nextInfoDialog(self):
		self.infoDialog.hide()
		if (not self.infoDialog2):
			self.infoDialog2 = Dialog(self.parent, 'information_dialog_2')
			self.infoDialog2.addTextBox('In order to play: ', pos=Vec3(0,0, 0.30))
			self.infoDialog2.addTextBox('Click a unit and then click the attack command to make him attack another enemy unit or enemy building.', pos=Vec3(0, 0, 0.20))
			self.infoDialog2.addTextBox('Click an SCV and then click the attack command to make him mine minerals or vespene.', pos=Vec3(0, 0, -0.05))
			self.infoDialog2.addOkCancel(self.infoDialog2.hide, only='ok')
		else:
			self.infoDialog2.show()
			
	'''
		Destroys this instance of the SinglePlayer
	'''
	def destroy(self):
		if (self.infoDialog):
			self.infoDialog.removeNode()
		if (self.infoDialog2):
			self.infoDialog2.removeNode()
		'''	
		for row in self.map:
			for col in row:
				col.removeNode()
				
		for key in self.resources.keys():
			self.resources[key].removeNode()
			
		for key in self.buildings.keys():
			self.buildings[key].removeNode()
			
		for key in self.units.keys():
			self.units[key].removeNode()
			
		for key in self.enemyUnits.keys():
			self.enemyUnits[key].removeNode()
			
		for key in self.enemyBuildings.keys():
			self.enemyBuildings[key].removeNode()
		'''
		self.gameStats.destroy()
		self.objectModel.removeNode()
		self.mapDummy.removeNode()
		self.parent.cameraController.endMouseWatch()
		
		
	def removeItem(self, key):
		type = key.split('_')[0]
		dict = None
		if (type == 'building'):
			dict = self.buildings
		elif (type == 'resource'):
			dict = self.resources
		elif (type == 'enemyunit'):
			dict = self.enemyUnits
		elif (type == 'enemybuilding'):
			dict = self.enemyBuildings
		elif (type == 'unit'):
			dict = self.units
		
		if (dict):
			newDict = {}
			for k, v in dict.items():
				if (k != key):
					newDict[k] = v
			
			dict = newDict

		
		
		